package goals;

import control.GUI.Output;

import utility.Vector;
import world.Person.KnownPerson;
import world.Person;
import world.PhysicalObject;

public abstract class Goal {
	protected Person self;
	protected double timeLeft;
	protected int priority;
	protected int reevaluationTime;

	public Goal(Person self, int priority) {
		this.self = self;
		this.priority = priority;
		this.timeLeft = 1;
	}

	/**
	 * @return true if the goal can be removed from the list of goals (goal
	 *         reached or not relevant anymore)
	 */
	public abstract boolean act(Output out);

	/**
	 * @return the time span (in ticks) that this type of goal can stay in the
	 *         list of goals without being reevaluated
	 */
	public abstract int getReevaluationTimeSpan();

	/*
	 * utility methods that are used by the classes extending Goal
	 */

	/**
	 * @param target
	 * @param direction
	 *            positive or zero: towards target; negative: away from target
	 */
	private void moveRelative(PhysicalObject target, int direction) {
		Vector movement = new Vector();

		movement.set(target.getPosition());
		movement=movement.subtract(self.getPosition());
		double travelDistance = self.getSpeed() * timeLeft;
		if (movement.length() >= travelDistance) {
			movement = movement.multiply(travelDistance / movement.length());
			timeLeft = 0;
		} else {
			timeLeft -= movement.length() / self.getSpeed();
		}
		if (direction >= 0) {
			self.setPosition(self.getPosition().add(movement));
		} else {
			self.setPosition(self.getPosition().subtract(movement));
		}
	}

	protected void moveTowards(PhysicalObject target) {
		moveRelative(target, 1);
	}

	protected void moveAway(PhysicalObject target) {
		moveRelative(target, -1);
	}

	protected void attack(PhysicalObject target, Output out) {
		if (self.getPosition().distance(target.getPosition()) < self
				.getAttackRange()) {
			out.push(self.getName() + " attacks " + target.getName() + " for "
					+ (int) self.getAttackDamage() + " damage\n");

			if (target instanceof Person) {
				Person targetPerson = (Person) target;
				targetPerson.dealtDamage(self.getAttackDamage());
				out.push(targetPerson.getName() + " has "
						+ (int) targetPerson.getHealth() + " health left\n");
				if (targetPerson.getHealth() < 0) {
					out.push(targetPerson.getName() + " dies\n");
					self.deleteKnownPerson(targetPerson);
					targetPerson.die();
				} else {
					KnownPerson kp = targetPerson.checkForKnownPerson(self);
					if (kp != null) {
						kp.friendliness = -1;
					}
				}
			}
		}
	}
	
	/**
	 * Return the target that is contained in this goal, if applicable.
	 */
	public abstract PhysicalObject getTarget();
}
